define(["jqplot"], function (require, exports, module) {
    var jQuery = require("jquery");
    /**
     * jqPlot
     * Pure JavaScript plotting plugin using jQuery
     *
     * Version: 1.0.8
     * Revision: 1250
     *
     * Copyright (c) 2009-2013 Chris Leonello
     * jqPlot is currently available for use in all personal or commercial projects
     * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
     * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
     * choose the license that best suits your project and use it accordingly.
     *
     * Although not required, the author would appreciate an email letting him
     * know of any substantial use of jqPlot.  You can reach the author at:
     * chris at jqplot dot com or see http://www.jqplot.com/info.php .
     *
     * If you are feeling kind and generous, consider supporting the project by
     * making a donation at: http://www.jqplot.com/donate.php .
     *
     * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
     *
     *     version 2007.04.27
     *     author Ash Searle
     *     http://hexmen.com/blog/2007/03/printf-sprintf/
     *     http://hexmen.com/js/sprintf.js
     *     The author (Ash Searle) has placed this code in the public domain:
     *     "This code is unrestricted: you are free to use it however you like."
     *
     */
    (function ($) {
        /**
         *  class: $.jqplot.CategoryAxisRenderer
         *  A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series.
         *
         *  To use this renderer, include the plugin in your source
         *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script>
         *
         *  and supply the appropriate options to your plot
         *
         *  > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}}
         **/
        $.jqplot.CategoryAxisRenderer = function (options) {
            $.jqplot.LinearAxisRenderer.call(this);
            // prop: sortMergedLabels
            // True to sort tick labels when labels are created by merging
            // x axis values from multiple series.  That is, say you have
            // two series like:
            // > line1 = [[2006, 4],            [2008, 9], [2009, 16]];
            // > line2 = [[2006, 3], [2007, 7], [2008, 6]];
            // If no label array is specified, tick labels will be collected
            // from the x values of the series.  With sortMergedLabels
            // set to true, tick labels will be:
            // > [2006, 2007, 2008, 2009]
            // With sortMergedLabels set to false, tick labels will be:
            // > [2006, 2008, 2009, 2007]
            //
            // Note, this property is specified on the renderOptions for the
            // axes when creating a plot:
            // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
            this.sortMergedLabels = false;
        };

        $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
        $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer;

        $.jqplot.CategoryAxisRenderer.prototype.init = function (options) {
            this.groups = 1;
            this.groupLabels = [];
            this._groupLabels = [];
            this._grouped = false;
            this._barsPerGroup = null;
            this.reverse = false;
            // prop: tickRenderer
            // A class of a rendering engine for creating the ticks labels displayed on the plot,
            // See <$.jqplot.AxisTickRenderer>.
            // this.tickRenderer = $.jqplot.AxisTickRenderer;
            // this.labelRenderer = $.jqplot.AxisLabelRenderer;
            $.extend(true, this, {tickOptions: {formatString: '%d'}}, options);
            var db = this._dataBounds;
            // Go through all the series attached to this axis and find
            // the min/max bounds for this axis.
            for (var i = 0; i < this._series.length; i++) {
                var s = this._series[i];
                if (s.groups) {
                    this.groups = s.groups;
                }
                var d = s.data;

                for (var j = 0; j < d.length; j++) {
                    if (this.name == 'xaxis' || this.name == 'x2axis') {
                        if (d[j][0] < db.min || db.min == null) {
                            db.min = d[j][0];
                        }
                        if (d[j][0] > db.max || db.max == null) {
                            db.max = d[j][0];
                        }
                    }
                    else {
                        if (d[j][1] < db.min || db.min == null) {
                            db.min = d[j][1];
                        }
                        if (d[j][1] > db.max || db.max == null) {
                            db.max = d[j][1];
                        }
                    }
                }
            }

            if (this.groupLabels.length) {
                this.groups = this.groupLabels.length;
            }
        };


        $.jqplot.CategoryAxisRenderer.prototype.createTicks = function () {
            // we're are operating on an axis here
            var ticks = this._ticks;
            var userTicks = this.ticks;
            var name = this.name;
            // databounds were set on axis initialization.
            var db = this._dataBounds;
            var dim, interval;
            var min, max;
            var pos1, pos2;
            var tt, i;

            // if we already have ticks, use them.
            if (userTicks.length) {
                // adjust with blanks if we have groups
                if (this.groups > 1 && !this._grouped) {
                    var l = userTicks.length;
                    var skip = parseInt(l / this.groups, 10);
                    var count = 0;
                    for (var i = skip; i < l; i += skip) {
                        userTicks.splice(i + count, 0, ' ');
                        count++;
                    }
                    this._grouped = true;
                }
                this.min = 0.5;
                this.max = userTicks.length + 0.5;
                var range = this.max - this.min;
                this.numberTicks = 2 * userTicks.length + 1;
                for (i = 0; i < userTicks.length; i++) {
                    tt = this.min + 2 * i * range / (this.numberTicks - 1);
                    // need a marker before and after the tick
                    var t = new this.tickRenderer(this.tickOptions);
                    t.showLabel = false;
                    // t.showMark = true;
                    t.setTick(tt, this.name);
                    this._ticks.push(t);
                    var t = new this.tickRenderer(this.tickOptions);
                    t.label = userTicks[i];
                    // t.showLabel = true;
                    t.showMark = false;
                    t.showGridline = false;
                    t.setTick(tt + 0.5, this.name);
                    this._ticks.push(t);
                }
                // now add the last tick at the end
                var t = new this.tickRenderer(this.tickOptions);
                t.showLabel = false;
                // t.showMark = true;
                t.setTick(tt + 1, this.name);
                this._ticks.push(t);
            }

            // we don't have any ticks yet, let's make some!
            else {
                if (name == 'xaxis' || name == 'x2axis') {
                    dim = this._plotDimensions.width;
                }
                else {
                    dim = this._plotDimensions.height;
                }

                // if min, max and number of ticks specified, user can't specify interval.
                if (this.min != null && this.max != null && this.numberTicks != null) {
                    this.tickInterval = null;
                }

                // if max, min, and interval specified and interval won't fit, ignore interval.
                if (this.min != null && this.max != null && this.tickInterval != null) {
                    if (parseInt((this.max - this.min) / this.tickInterval, 10) != (this.max - this.min) / this.tickInterval) {
                        this.tickInterval = null;
                    }
                }

                // find out how many categories are in the lines and collect labels
                var labels = [];
                var numcats = 0;
                var min = 0.5;
                var max, val;
                var isMerged = false;
                for (var i = 0; i < this._series.length; i++) {
                    var s = this._series[i];
                    for (var j = 0; j < s.data.length; j++) {
                        if (this.name == 'xaxis' || this.name == 'x2axis') {
                            val = s.data[j][0];
                        }
                        else {
                            val = s.data[j][1];
                        }
                        if ($.inArray(val, labels) == -1) {
                            isMerged = true;
                            numcats += 1;
                            labels.push(val);
                        }
                    }
                }

                if (isMerged && this.sortMergedLabels) {
                    if (typeof labels[0] == "string") {
                        labels.sort();
                    } else {
                        labels.sort(function (a, b) {
                            return a - b;
                        });
                    }
                }

                // keep a reference to these tick labels to use for redrawing plot (see bug #57)
                this.ticks = labels;

                // now bin the data values to the right lables.
                for (var i = 0; i < this._series.length; i++) {
                    var s = this._series[i];
                    for (var j = 0; j < s.data.length; j++) {
                        if (this.name == 'xaxis' || this.name == 'x2axis') {
                            val = s.data[j][0];
                        }
                        else {
                            val = s.data[j][1];
                        }
                        // for category axis, force the values into category bins.
                        // we should have the value in the label array now.
                        var idx = $.inArray(val, labels) + 1;
                        if (this.name == 'xaxis' || this.name == 'x2axis') {
                            s.data[j][0] = idx;
                        }
                        else {
                            s.data[j][1] = idx;
                        }
                    }
                }

                // adjust with blanks if we have groups
                if (this.groups > 1 && !this._grouped) {
                    var l = labels.length;
                    var skip = parseInt(l / this.groups, 10);
                    var count = 0;
                    for (var i = skip; i < l; i += skip + 1) {
                        labels[i] = ' ';
                    }
                    this._grouped = true;
                }

                max = numcats + 0.5;
                if (this.numberTicks == null) {
                    this.numberTicks = 2 * numcats + 1;
                }

                var range = max - min;
                this.min = min;
                this.max = max;
                var track = 0;

                // todo: adjust this so more ticks displayed.
                var maxVisibleTicks = parseInt(3 + dim / 10, 10);
                var skip = parseInt(numcats / maxVisibleTicks, 10);

                if (this.tickInterval == null) {

                    this.tickInterval = range / (this.numberTicks - 1);

                }
                // if tickInterval is specified, we will ignore any computed maximum.
                for (var i = 0; i < this.numberTicks; i++) {
                    tt = this.min + i * this.tickInterval;
                    var t = new this.tickRenderer(this.tickOptions);
                    // if even tick, it isn't a category, it's a divider
                    if (i / 2 == parseInt(i / 2, 10)) {
                        t.showLabel = false;
                        t.showMark = true;
                    }
                    else {
                        if (skip > 0 && track < skip) {
                            t.showLabel = false;
                            track += 1;
                        }
                        else {
                            t.showLabel = true;
                            track = 0;
                        }
                        t.label = t.formatter(t.formatString, labels[(i - 1) / 2]);
                        t.showMark = false;
                        t.showGridline = false;
                    }
                    t.setTick(tt, this.name);
                    this._ticks.push(t);
                }
            }

        };

        // called with scope of axis
        $.jqplot.CategoryAxisRenderer.prototype.draw = function (ctx, plot) {
            if (this.show) {
                // populate the axis label and value properties.
                // createTicks is a method on the renderer, but
                // call it within the scope of the axis.
                this.renderer.createTicks.call(this);
                // fill a div with axes labels in the right direction.
                // Need to pregenerate each axis to get its bounds and
                // position it and the labels correctly on the plot.
                var dim = 0;
                var temp;
                // Added for theming.
                if (this._elem) {
                    // this._elem.empty();
                    // Memory Leaks patch
                    this._elem.emptyForce();
                }

                this._elem = this._elem || $('<div class="jqplot-axis jqplot-' + this.name + '" style="position:absolute;"></div>');

                if (this.name == 'xaxis' || this.name == 'x2axis') {
                    this._elem.width(this._plotDimensions.width);
                }
                else {
                    this._elem.height(this._plotDimensions.height);
                }

                // create a _label object.
                this.labelOptions.axis = this.name;
                this._label = new this.labelRenderer(this.labelOptions);
                if (this._label.show) {
                    var elem = this._label.draw(ctx, plot);
                    elem.appendTo(this._elem);
                }

                var t = this._ticks;
                for (var i = 0; i < t.length; i++) {
                    var tick = t[i];
                    if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
                        var elem = tick.draw(ctx, plot);
                        elem.appendTo(this._elem);
                    }
                }

                this._groupLabels = [];
                // now make group labels
                for (var i = 0; i < this.groupLabels.length; i++) {
                    var elem = $('<div style="position:absolute;" class="jqplot-' + this.name + '-groupLabel"></div>');
                    elem.html(this.groupLabels[i]);
                    this._groupLabels.push(elem);
                    elem.appendTo(this._elem);
                }
            }
            return this._elem;
        };

        // called with scope of axis
        $.jqplot.CategoryAxisRenderer.prototype.set = function () {
            var dim = 0;
            var temp;
            var w = 0;
            var h = 0;
            var lshow = (this._label == null) ? false : this._label.show;
            if (this.show) {
                var t = this._ticks;
                for (var i = 0; i < t.length; i++) {
                    var tick = t[i];
                    if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
                        if (this.name == 'xaxis' || this.name == 'x2axis') {
                            temp = tick._elem.outerHeight(true);
                        }
                        else {
                            temp = tick._elem.outerWidth(true);
                        }
                        if (temp > dim) {
                            dim = temp;
                        }
                    }
                }

                var dim2 = 0;
                for (var i = 0; i < this._groupLabels.length; i++) {
                    var l = this._groupLabels[i];
                    if (this.name == 'xaxis' || this.name == 'x2axis') {
                        temp = l.outerHeight(true);
                    }
                    else {
                        temp = l.outerWidth(true);
                    }
                    if (temp > dim2) {
                        dim2 = temp;
                    }
                }

                if (lshow) {
                    w = this._label._elem.outerWidth(true);
                    h = this._label._elem.outerHeight(true);
                }
                if (this.name == 'xaxis') {
                    dim += dim2 + h;
                    this._elem.css({'height': dim + 'px', left: '0px', bottom: '0px'});
                }
                else if (this.name == 'x2axis') {
                    dim += dim2 + h;
                    this._elem.css({'height': dim + 'px', left: '0px', top: '0px'});
                }
                else if (this.name == 'yaxis') {
                    dim += dim2 + w;
                    this._elem.css({'width': dim + 'px', left: '0px', top: '0px'});
                    if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
                        this._label._elem.css('width', w + 'px');
                    }
                }
                else {
                    dim += dim2 + w;
                    this._elem.css({'width': dim + 'px', right: '0px', top: '0px'});
                    if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
                        this._label._elem.css('width', w + 'px');
                    }
                }
            }
        };

        // called with scope of axis
        $.jqplot.CategoryAxisRenderer.prototype.pack = function (pos, offsets) {
            var ticks = this._ticks;
            var max = this.max;
            var min = this.min;
            var offmax = offsets.max;
            var offmin = offsets.min;
            var lshow = (this._label == null) ? false : this._label.show;
            var i;

            for (var p in pos) {
                this._elem.css(p, pos[p]);
            }

            this._offsets = offsets;
            // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
            var pixellength = offmax - offmin;
            var unitlength = max - min;

            if (!this.reverse) {
                // point to unit and unit to point conversions references to Plot DOM element top left corner.

                this.u2p = function (u) {
                    return (u - min) * pixellength / unitlength + offmin;
                };

                this.p2u = function (p) {
                    return (p - offmin) * unitlength / pixellength + min;
                };

                if (this.name == 'xaxis' || this.name == 'x2axis') {
                    this.series_u2p = function (u) {
                        return (u - min) * pixellength / unitlength;
                    };
                    this.series_p2u = function (p) {
                        return p * unitlength / pixellength + min;
                    };
                }

                else {
                    this.series_u2p = function (u) {
                        return (u - max) * pixellength / unitlength;
                    };
                    this.series_p2u = function (p) {
                        return p * unitlength / pixellength + max;
                    };
                }
            }

            else {
                // point to unit and unit to point conversions references to Plot DOM element top left corner.

                this.u2p = function (u) {
                    return offmin + (max - u) * pixellength / unitlength;
                };

                this.p2u = function (p) {
                    return min + (p - offmin) * unitlength / pixellength;
                };

                if (this.name == 'xaxis' || this.name == 'x2axis') {
                    this.series_u2p = function (u) {
                        return (max - u) * pixellength / unitlength;
                    };
                    this.series_p2u = function (p) {
                        return p * unitlength / pixellength + max;
                    };
                }

                else {
                    this.series_u2p = function (u) {
                        return (min - u) * pixellength / unitlength;
                    };
                    this.series_p2u = function (p) {
                        return p * unitlength / pixellength + min;
                    };
                }

            }


            if (this.show) {
                if (this.name == 'xaxis' || this.name == 'x2axis') {
                    for (i = 0; i < ticks.length; i++) {
                        var t = ticks[i];
                        if (t.show && t.showLabel) {
                            var shim;

                            if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
                                // will need to adjust auto positioning based on which axis this is.
                                var temp = (this.name == 'xaxis') ? 1 : -1;
                                switch (t.labelPosition) {
                                    case 'auto':
                                        // position at end
                                        if (temp * t.angle < 0) {
                                            shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
                                        }
                                        // position at start
                                        else {
                                            shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
                                        }
                                        break;
                                    case 'end':
                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
                                        break;
                                    case 'start':
                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
                                        break;
                                    case 'middle':
                                        shim = -t.getWidth() / 2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
                                        break;
                                    default:
                                        shim = -t.getWidth() / 2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
                                        break;
                                }
                            }
                            else {
                                shim = -t.getWidth() / 2;
                            }
                            var val = this.u2p(t.value) + shim + 'px';
                            t._elem.css('left', val);
                            t.pack();
                        }
                    }

                    var labeledge = ['bottom', 0];
                    if (lshow) {
                        var w = this._label._elem.outerWidth(true);
                        this._label._elem.css('left', offmin + pixellength / 2 - w / 2 + 'px');
                        if (this.name == 'xaxis') {
                            this._label._elem.css('bottom', '0px');
                            labeledge = ['bottom', this._label._elem.outerHeight(true)];
                        }
                        else {
                            this._label._elem.css('top', '0px');
                            labeledge = ['top', this._label._elem.outerHeight(true)];
                        }
                        this._label.pack();
                    }

                    // draw the group labels
                    var step = parseInt(this._ticks.length / this.groups, 10) + 1;
                    for (i = 0; i < this._groupLabels.length; i++) {
                        var mid = 0;
                        var count = 0;
                        for (var j = i * step; j < (i + 1) * step; j++) {
                            if (j >= this._ticks.length - 1) continue; // the last tick does not exist as there is no other group in order to have an empty one.
                            if (this._ticks[j]._elem && this._ticks[j].label != " ") {
                                var t = this._ticks[j]._elem;
                                var p = t.position();
                                mid += p.left + t.outerWidth(true) / 2;
                                count++;
                            }
                        }
                        mid = mid / count;
                        this._groupLabels[i].css({'left': (mid - this._groupLabels[i].outerWidth(true) / 2)});
                        this._groupLabels[i].css(labeledge[0], labeledge[1]);
                    }
                }
                else {
                    for (i = 0; i < ticks.length; i++) {
                        var t = ticks[i];
                        if (t.show && t.showLabel) {
                            var shim;
                            if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
                                // will need to adjust auto positioning based on which axis this is.
                                var temp = (this.name == 'yaxis') ? 1 : -1;
                                switch (t.labelPosition) {
                                    case 'auto':
                                    // position at end
                                    case 'end':
                                        if (temp * t.angle < 0) {
                                            shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
                                        }
                                        else {
                                            shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
                                        }
                                        break;
                                    case 'start':
                                        if (t.angle > 0) {
                                            shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
                                        }
                                        else {
                                            shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
                                        }
                                        break;
                                    case 'middle':
                                        // if (t.angle > 0) {
                                        //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
                                        // }
                                        // else {
                                        //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
                                        // }
                                        shim = -t.getHeight() / 2;
                                        break;
                                    default:
                                        shim = -t.getHeight() / 2;
                                        break;
                                }
                            }
                            else {
                                shim = -t.getHeight() / 2;
                            }

                            var val = this.u2p(t.value) + shim + 'px';
                            t._elem.css('top', val);
                            t.pack();
                        }
                    }

                    var labeledge = ['left', 0];
                    if (lshow) {
                        var h = this._label._elem.outerHeight(true);
                        this._label._elem.css('top', offmax - pixellength / 2 - h / 2 + 'px');
                        if (this.name == 'yaxis') {
                            this._label._elem.css('left', '0px');
                            labeledge = ['left', this._label._elem.outerWidth(true)];
                        }
                        else {
                            this._label._elem.css('right', '0px');
                            labeledge = ['right', this._label._elem.outerWidth(true)];
                        }
                        this._label.pack();
                    }

                    // draw the group labels, position top here, do left after label position.
                    var step = parseInt(this._ticks.length / this.groups, 10) + 1; // step is one more than before as we don't want to have overlaps in loops
                    for (i = 0; i < this._groupLabels.length; i++) {
                        var mid = 0;
                        var count = 0;
                        for (var j = i * step; j < (i + 1) * step; j++) { // j must never reach (i+1)*step as we don't want to have overlap between loops
                            if (j >= this._ticks.length - 1) continue; // the last tick does not exist as there is no other group in order to have an empty one.
                            if (this._ticks[j]._elem && this._ticks[j].label != " ") {
                                var t = this._ticks[j]._elem;
                                var p = t.position();
                                mid += p.top + t.outerHeight() / 2;
                                count++;
                            }
                        }
                        mid = mid / count;
                        this._groupLabels[i].css({'top': mid - this._groupLabels[i].outerHeight() / 2});
                        this._groupLabels[i].css(labeledge[0], labeledge[1]);

                    }
                }
            }
        };


    })(jQuery);
});

